#include "lxz_config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#ifdef LXZ_CFG_OS_WIN32
  #define _WIN32_WINNT 0x400
  #include <direct.h>
  #include <io.h>
  #include <windows.h>
  #include <errno.h>
#endif /* LXZ_CFG_OS_WIN32 */

#ifdef LXZ_CFG_OS_LINUX
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <arpa/inet.h>
#endif /* LXZ_CFG_OS_LINUX */

#include "lxz_types.h"
#include "lxz_string.h"
#include "lxz_runlog.h"
#include "lxz_debug.h"
#include "lxz_exec.h"
#include "os_port.h"

#include "udtserver.h"

/*
 * The structure of version string
 * -- Major.Minor.RefixID
 */
#define SWR_SVR_VERSION_FULL "SWRServer-2.0.0"
#define SWR_SVR_COMPILE_DATE (__DATE__)
#define SWR_SVR_COMPILE_TIME (__TIME__)

static uint32 ThreadTCPService(void* lpParam);
static uint32 ThreadUDPService(void* lpParam);

static uint32 udt_f_tcpserver_SendRecved(void* pctxt);
static sint32 udt_f_tcpserver_start(uint16 u_svr_port);
static sint32 udt_f_udpserver_start(uint16 u_svr_port);

static sint32 udt_f_logfile_new(void* arg);


static sint32 udt_f_tcpserver_start(uint16 u_svr_port)
{
    sint32 i_ret_val = 0;

    sint32 i_op_status = 0;
    sint32 i_svr_socket = 0;
    sint32 i_new_socket = 0;
    udt_context_t * p_udt_ctxt = NULL;

    if((i_svr_socket = osp_socket_f_open(AF_INET,SOCK_STREAM,0)) == 0)
    {
        OS_DBG_URC(("tcpserver: osp_socket_f_open fail;\r\n"));
        return -1;
    }

    if(osp_socket_f_bind(i_svr_socket, u_svr_port) == 0)
    {
        OS_DBG_URC(("tcpserver: osp_socket_f_bind fail;\r\n"));
        osp_socket_f_close(i_svr_socket);
        return -1;
    }

    if(osp_socket_f_listen(i_svr_socket, UDT_MAX_BACK_LOG) == 0)
    {
        OS_DBG_URC(("tcpserver: osp_socket_f_listen fail;\r\n"));
        osp_socket_f_close(i_svr_socket);
        return -1;
    }

    while (1)
    {
        p_udt_ctxt = (udt_context_t *)malloc(sizeof(udt_context_t));
        if(p_udt_ctxt == NULL)
        {
            i_ret_val = -1;
            break;
        }
        
        memset(p_udt_ctxt, 0, sizeof(udt_context_t));
        p_udt_ctxt->p_rw_buff = (sint08 *)malloc(UDT_DEF_BUFF_SIZE);
        if(p_udt_ctxt->p_rw_buff == NULL)
        {
            osp_socket_f_close(i_svr_socket);
            free(p_udt_ctxt);

            i_ret_val = -1;
            break;
        }

        if ((i_new_socket = osp_socket_f_accept(i_svr_socket))==0)
        {
            osp_socket_f_close(i_svr_socket);
            free(p_udt_ctxt->p_rw_buff);
            free(p_udt_ctxt);

            i_ret_val = -1;
            break;
        }
        
        p_udt_ctxt->lsockfd = i_svr_socket;
        p_udt_ctxt->csockfd = i_new_socket;
        
        i_op_status = osp_thread_f_open(ThreadTCPService, p_udt_ctxt,
            &(p_udt_ctxt->iThreadID));
        
        if (i_op_status == 0)
        {
            osp_socket_f_close(p_udt_ctxt->csockfd);
            free(p_udt_ctxt->p_rw_buff);
            free(p_udt_ctxt);
        }
    }

    return i_ret_val;
}

static sint32 udt_f_udpserver_start(uint16 u_svr_port)
{
    sint32 i_ret_val = 0;

    sint32 i_op_status = 0;
    sint32 i_svr_socket = 0;
    udt_context_t * p_udt_ctxt = NULL;

    p_udt_ctxt = (udt_context_t *)malloc(sizeof(udt_context_t));
    if(p_udt_ctxt == NULL)
    {
        return -1;
    }

    memset(p_udt_ctxt, 0, sizeof(udt_context_t));
    p_udt_ctxt->p_rw_buff = (sint08 *)malloc(UDT_MAX_BUFF_SIZE);
    if(p_udt_ctxt->p_rw_buff == NULL)
    {
        free(p_udt_ctxt);
        return -1;
    }

    if((i_svr_socket = osp_socket_f_open(AF_INET,SOCK_DGRAM,0)) == 0)
    {
        OS_DBG_URC(("udpserver: osp_socket_f_open fail;\r\n"));
        free(p_udt_ctxt->p_rw_buff);
        free(p_udt_ctxt);
        return -1;
    }

    if(osp_socket_f_bind(i_svr_socket, u_svr_port) == 0)
    {
        OS_DBG_URC(("udpserver: osp_socket_f_bind fail;\r\n"));
        osp_socket_f_close(i_svr_socket);
        free(p_udt_ctxt->p_rw_buff);
        free(p_udt_ctxt);
        return -1;
    }

    p_udt_ctxt->lsockfd = i_svr_socket;
    p_udt_ctxt->csockfd = i_svr_socket;

    i_op_status = osp_thread_f_open(ThreadUDPService, p_udt_ctxt,
        &(p_udt_ctxt->iThreadID));
    
    if (i_op_status == 0)
    {
        osp_socket_f_close(p_udt_ctxt->csockfd);
        free(p_udt_ctxt->p_rw_buff);
        free(p_udt_ctxt);
        p_udt_ctxt = NULL;
    }

    return i_ret_val;
}

static uint32 udt_f_tcpserver_SendRecved(void* pctxt)
{
    udt_context_t * p_udt_ctxt = (udt_context_t *)pctxt;

    sint32 i_recv_len = 0;
    sint32 i_echo_times = 0;

    OS_DBG_LOG(("iID:%u, TCPEcho service, start.\r\n", p_udt_ctxt->iThreadID));
    OS_DBG_URC(("iID:%u, TCPEcho service, start.\r\n", p_udt_ctxt->iThreadID));
    while (1)
    {
        memset(p_udt_ctxt->p_rw_buff, 0, UDT_DEF_BUFF_SIZE);
        i_recv_len = osp_socket_f_read(p_udt_ctxt->csockfd,
            p_udt_ctxt->p_rw_buff, UDT_DEF_BUFF_SIZE);

        if(i_recv_len > 0)
        {
            i_echo_times++;
            OS_DBG_LOG(("iID:%u, TCPEcho service, times=%d.\r\n",
                p_udt_ctxt->iThreadID, i_echo_times));

            osp_socket_f_write(p_udt_ctxt->csockfd, 
                p_udt_ctxt->p_rw_buff, i_recv_len);
        }
        else
        {
            break;
        }
        
        osp_timer_f_sleep(10);
    }

    OS_DBG_LOG(("iID:%u, TCPEcho service, stop.\r\n", p_udt_ctxt->iThreadID));
    OS_DBG_URC(("iID:%u, TCPEcho service, stop.\r\n", p_udt_ctxt->iThreadID));

    osp_socket_f_close(p_udt_ctxt->csockfd);
    free(p_udt_ctxt->p_rw_buff);
    free(p_udt_ctxt);

    return 0;
}

static uint32 ThreadTCPService(void* lpParam)
{
    uint32 u_thread_id = 0;
    udt_context_t * p_udt_ctxt = (udt_context_t *)lpParam;
    
    /* Send What Received, all data that from peer will send to peer */
    if (p_udt_ctxt != NULL)
    {
        u_thread_id = p_udt_ctxt->iThreadID;
        OS_DBG_LOG(("iID:%u, TCPService, start!\r\n", u_thread_id));
        OS_DBG_URC(("iID:%u, TCPService, start!\r\n", u_thread_id));

        udt_f_tcpserver_SendRecved(p_udt_ctxt);
        
        OS_DBG_LOG(("iID:%u, TCPService, stop!\r\n", u_thread_id));
        OS_DBG_URC(("iID:%u, TCPService, stop!\r\n", u_thread_id));
    }

    return 0;
}

static uint32 ThreadUDPService(void* lpParam)
{
    sint32 i_recv_len = 0;
    sint32 i_echo_times = 0;

    int lenfrom;
    struct sockaddr addrfrom;
    struct sockaddr_in* p_in_addr;
    sint08 * p_peer_ipaddr = NULL;

    udt_context_t * p_udt_ctxt = (udt_context_t *)lpParam;

    if (p_udt_ctxt != NULL)
    {
        OS_DBG_LOG(("iID:%u, UDPEcho service, start!\r\n", p_udt_ctxt->iThreadID));
        OS_DBG_URC(("iID:%u, UDPEcho service, start!\r\n", p_udt_ctxt->iThreadID));
        while (1)
        {
            lenfrom = sizeof(struct sockaddr);
            memset(&addrfrom, 0, sizeof(struct sockaddr));
            memset(p_udt_ctxt->p_rw_buff, 0, UDT_MAX_BUFF_SIZE);
            
            i_recv_len = osp_socket_f_readfrom(p_udt_ctxt->csockfd,
                p_udt_ctxt->p_rw_buff, UDT_MAX_BUFF_SIZE, &addrfrom, &lenfrom);
            
            if(i_recv_len < 0)
            {
                OS_DBG_DBG(("UDP readfrom error:%d;\r\n", osp_socket_f_geterror()));
                break;
            }
            
            i_echo_times++;
            p_in_addr = (struct sockaddr_in*)(&(addrfrom));
            p_peer_ipaddr = inet_ntoa(p_in_addr->sin_addr);
            OS_DBG_LOG(("iID:%u, UDPEcho service, peer=%s, times=%d.\r\n",
                p_udt_ctxt->iThreadID, p_peer_ipaddr, i_echo_times));
            
            osp_socket_f_writeto(p_udt_ctxt->csockfd, 
                p_udt_ctxt->p_rw_buff, i_recv_len, &addrfrom, lenfrom);
            
            osp_timer_f_sleep(10);
        }
        
        OS_DBG_LOG(("iID:%u, UDPEcho service, stop!\r\n", p_udt_ctxt->iThreadID));
        OS_DBG_URC(("iID:%u, UDPEcho service, stop!\r\n", p_udt_ctxt->iThreadID));
        
        osp_socket_f_close(p_udt_ctxt->csockfd);
        free(p_udt_ctxt->p_rw_buff);
        free(p_udt_ctxt);
    }

    return 0;
}

static sint32 udt_f_logfile_new(void* arg)
{
    sint32 i_ret_val = 1;

    lxz_log_f_init(arg);

    return 0;
}

int udt_server_main(uint16 u_svr_port)
{
    sint08 * p_root_directory = NULL;

    OS_DBG_LOG(("<--Author: numax; Version: %s-->\r\n", SWR_SVR_VERSION_FULL));
    OS_DBG_LOG(("Usage:  Update time: %s %s\r\n", SWR_SVR_COMPILE_DATE, SWR_SVR_COMPILE_TIME));
    OS_DBG_LOG(("UDP-server: echo-service only;\r\n"));
    OS_DBG_LOG(("TCP-server: echo-service only;\r\n"));
    OS_DBG_LOG(("\r\n"));

    OS_DBG_URC(("<--Author: numax; Version: %s-->\r\n", SWR_SVR_VERSION_FULL));
    OS_DBG_URC(("Usage:  Update time: %s %s\r\n", SWR_SVR_COMPILE_DATE, SWR_SVR_COMPILE_TIME));
    OS_DBG_URC(("UDP-server: echo-service only;\r\n"));
    OS_DBG_URC(("TCP-server: echo-service only;\r\n"));
    OS_DBG_URC(("\r\n"));
    OS_DBG_URC(("Listening on %d\r\n\r\n", u_svr_port));

    p_root_directory = (uint08 *)malloc(LXZ_PATH_LENGTH_MAX);
    memset(p_root_directory, 0, LXZ_PATH_LENGTH_MAX);
    strcpy(p_root_directory, lxz_root_directory_f_get());
    lxz_exec_f_open(udt_f_logfile_new, p_root_directory, LXZ_LOGFILE_NEW_CYCLE);

    udt_f_udpserver_start(u_svr_port);
    udt_f_tcpserver_start(u_svr_port);

    free(p_root_directory);

    return 0;
}


